#pragma once
#include "hashlib.h"
#include "li_pipe.h"
#include "li_list.h"
#include "li_io.h"
Li_List lily_pipe_sources = NULL; //List<Pipe> source to produces data
Li_List lily_pipe_entries = NULL; //List<Pipe> receive data

const char* cmd_pipe_annotation = "pipeLine control:\n"
" list|find [pipe] :show available pipes\n"
" connect [source] [endpoint]:show connections, build or disconnect pipes";
int32_t cmd_pipe(int32_t n, char* arg[])
{
	if (n == 1 || (n == 2 && arg[1][0] == 'l')) // list all
	{
		int32_t i;
		Pipe* pipes = list_content(lily_pipe_sources, Pipe);
		// Pipe *pipes_ep = list_content(lily_pipe_entries, Pipe);
		lily_out(">pipe sources:\n");
		for (i = 0; i < lily_pipe_sources->count; i++)
		{
			lily_out(" ");
			lily_out(pipes[i]->id);
			lily_out("\n");
		}
		lily_out(">pipe entries:\n");
		pipes = list_content(lily_pipe_entries, Pipe);
		for (i = 0; i < lily_pipe_entries->count; i++)
		{
			lily_out(" ");
			lily_out(pipes[i]->id);
			lily_out("\n");
		}
		return 0;
	}
	//n>1
	if (arg[1][0] == 'l' || arg[1][0] == 'f') //n>2
	{
		Pipe p = pipe_find_source(arg[2]);
		if (!p)
		{
			p = pipe_find_entry(arg[2]);
			if (!p)
			{
				lily_out("pipe not found\n");
				return 0;
			}
			Pipe ps = pipe_find_connected_source(p);
			if (ps)
				lily_out(ps->id);
			lily_out(">");
			lily_out(p->id);
			lily_out("\n");
			return 0;
		}
		lily_out(p->id);
		lily_out(">");
		Pipe ps = pipe_find_connected_entry(p);
		if (ps)
			lily_out(ps->id);
		lily_out("\n");
		return 0;
	}
	if (arg[1][0] == 'c')
	{
		if (n == 2) // show connection
		{
			pipe_show_connection();
		}
		if (n == 3) // connect keyboard, disconnect
		{
			Pipe src = pipe_find_source(arg[2]);
			if (!src)
			{
				lily_out("source pipe not found\n");
				return 0;
			}
			src->streamLine = NULL;
			return 1;
		}
		else if (n == 4) // connect keyboard screen
		{
			Pipe src = pipe_find_source(arg[2]);
			if (!src)
			{
				lily_out("source pipe not found\n");
				return 0;
			}
			Pipe ety = pipe_find_entry(arg[3]);
			if (!ety)
			{
				lily_out("entry pipe not found\n");
				return 0;
			}
			pipe_connect_to(src, ety);
			return 1;
		}
		return 0;
	}

	li_error("no mode, use: pipe mode [arg]", -1);
	return -1;
}

// to create a new Pipe object
// pipe_type must either be 's' for PipeSource or 'e' for PipeEntry
// when create a Pipe Source the stream should be NULL
// while create a Pipe Entry the stream must be assigned
Pipe public_a_new_pipe(const char* id, void* arg, PipeLine stream, char pipe_type)
{
	if (pipe_type == 's')
	{
		if (lily_pipe_sources == NULL)
		{
			lily_pipe_sources = new_li(Pipe);
			if (!lily_pipe_sources)
				return NULL;
		}
	}
	else if (pipe_type == 'e')
	{
		if (lily_pipe_entries == NULL)
		{
			lily_pipe_entries = new_li(Pipe);
			if (!lily_pipe_entries)
				return NULL;
		}
	}
	else
	{
		return NULL;
	}

	Pipe pip = malloc(sizeof(Pipe_t));
	if (!pip)
		return NULL;

	pip->id = id;
	pip->arg = arg;
	pip->streamLine = stream;
	if (pipe_type == 's')
	{
		li_add(lily_pipe_sources, pip);
	}
	else
	{
		li_add(lily_pipe_entries, pip);
	}
	return pip;
}
// return a pipe source
Pipe new_input_device(const char* id)
{
	return public_a_new_pipe(id, NULL, NULL, 's');
}
//return a pipe entry
Pipe new_output_device(const char* id, void* arg, PipeLine stream)
{
	return public_a_new_pipe(id, arg, stream, 'e');
}

// delete a pipe from Lily
// use like this: remove_a_pipe(pipe,'s')
//					pipe = NULL;
int32_t remove_a_pipe(Pipe pipe, char pipe_type)
{
	if (!pipe)
		return -1;
	int32_t code = 0;
	if (pipe_type == 's')
	{
		if (!lily_pipe_sources)
			return -2;
		code = list_remove(lily_pipe_sources, &pipe);
	}
	else if (pipe_type == 'e')
	{
		if (!lily_pipe_entries)
			return -2;
		Pipe p;
		do
		{
			p = pipe_find_connected_source(pipe);
			if (!p)
				break;
			pipe_disconnect(p);
		} while (p != NULL);

		code = list_remove(lily_pipe_entries, &pipe);
	}
	if (code < 0)
		return code;
	free(pipe);

	return 0;
}
int32_t remove_a_pipe_by_name(char* pipe_name, char pipe_type)
{
	if (!pipe_name)
		return -1;
	int32_t code = -1;
	Pipe pipe = NULL;
	if (pipe_type == 's')
	{
		pipe = pipe_find_source(pipe_name);

		if (!lily_pipe_sources)
			return -2;
		code = list_remove(lily_pipe_sources, &pipe);
	}
	else if (pipe_type == 'e')
	{
		pipe = pipe_find_entry(pipe_name);
		if (!lily_pipe_entries)
			return -2;
		Pipe p;
		do
		{
			p = pipe_find_connected_source(pipe);
			if (!p)
				break;
			pipe_disconnect(p);
		} while (p != NULL);

		code = list_remove(lily_pipe_entries, &pipe);
	}
	if (code < 0)
		return code;
	free(pipe);

	return 0;
}

int32_t pipe_connect_to(Pipe source, Pipe entry)
{
	if (!source || !entry)
		return -1;
	source->arg = entry->arg;
	source->streamLine = entry->streamLine;
	return 0;
}
int32_t pipe_disconnect(Pipe source)
{
	if (!source)
		return -1;
	source->streamLine = NULL;
	source->arg = NULL;
	return 0;
}
// note only for source pipe
void* pipe_release_data(Pipe pipe, char* data, int32_t len)
{
	if (!pipe)
		return NULL;
	if (!pipe->streamLine)
		return NULL;
	return pipe->streamLine(pipe->arg, data, len);
}

Pipe pipe_find_source(const char* name)
{
	int32_t i;
	Pipe* pipes = list_content(lily_pipe_sources, Pipe);
	for (i = 0; i < lily_pipe_sources->count; i++)
	{
		if (strcmp(name, pipes[i]->id) == 0)
		{
			return pipes[i];
		}
	}
	return NULL;
}

Pipe pipe_find_entry(const char* name)
{
	int32_t i;
	Pipe* pipes = list_content(lily_pipe_entries, Pipe);
	for (i = 0; i < lily_pipe_entries->count; i++)
	{
		if (strcmp(name, pipes[i]->id) == 0)
		{
			return pipes[i];
		}
	}
	return NULL;
}

Pipe pipe_find_connected_entry(Pipe src)
{
	Pipe* ents = list_content(lily_pipe_entries, Pipe);
	int32_t i, n = lily_pipe_entries->count;
	for (i = 0; i < n; i++)
	{
		if (ents[i]->streamLine == src->streamLine && ents[i]->arg == src->arg)
		{
			return ents[i];
		}
	}
	return NULL;
}
// to find a pipe source which is connected to ent
// may more than one source pipe be connected
Pipe pipe_find_connected_source(Pipe ent)
{
	Pipe* srcs = list_content(lily_pipe_sources, Pipe);
	int32_t i, n = lily_pipe_sources->count;
	for (i = 0; i < n; i++)
	{
		if (srcs[i]->streamLine == ent->streamLine && srcs[i]->arg == ent->arg)
		{
			return srcs[i];
		}
	}
	return NULL;
}
void pipe_show_connection()
{
	Pipe* ents = list_content(lily_pipe_entries, Pipe);
	Pipe* srcs = list_content(lily_pipe_sources, Pipe);
	int32_t i;
	for (i = 0; i < lily_pipe_sources->count; i++)
	{
		lily_out(srcs[i]->id);
		lily_out(">");
		Pipe p = pipe_find_connected_entry(srcs[i]);
		if (p) // connected with an entry
		{
			lily_out(p->id);
		}
		lily_out("\n");
	}
	for (i = 0; i < lily_pipe_entries->count; i++)
	{
		Pipe p = pipe_find_connected_source(ents[i]);
		if (!p) // this entry has no connection
		{
			lily_out(" >");
			lily_out(ents[i]->id);
			lily_out("\n");
		}
	}
}